package juc;

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * 测试正确退出线程的方式
 *
 * @author hck soccerlover9527@gmail.com
 */
public class TestCancelThreadGraceful {
    public static void main(String[] args) {
        //  测试 volatile 退出线程
//        var cancelThreadWithVolatile = new CancelThreadWithVolatileTest();
//        cancelThreadWithVolatile.consumerAndCancel();

        //  测试 interrupt 退出线程
        var cancelThreadWithInterruptTest = new CancelThreadWithInterruptTest();
        cancelThreadWithInterruptTest.consumerAndCancel();
    }

    private static class CancelThreadWithVolatileTest implements ConsumeAndCancelProducerTest {
        private static final Integer QUEUE_SIZE = 10;
        private static final ArrayBlockingQueue<Integer> QUEUE = new ArrayBlockingQueue<>(QUEUE_SIZE);

        @Override
        @SuppressWarnings("all")
        public void consumerAndCancel() {
            var producer = new ProducerWithVolatileCancel();
            Thread producerThread = new Thread(producer);
            producerThread.start();
            //  等待生产者充满队列
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Random random = new Random(System.currentTimeMillis());
            System.out.println("consumer enter >>>");
            //  消费者随机退出
            while (random.nextInt(0, 100) > 10) {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("consumer consume:" + QUEUE.poll());
            }
            System.out.println("consumer exit <<<");
            System.out.println("notify producer exit start >>>");
            producer.cancel();
            System.out.println("notify producer exit end <<<");
            System.out.println("producer thread status:" + producerThread.getState());
        }

        static class ProducerWithVolatileCancel implements Runnable {
            private volatile boolean flag = true;

            public void cancel() {
                flag = false;
            }

            @Override
            public void run() {
                System.out.println("producer enter >>>");
                Random random = new Random(System.currentTimeMillis());
                try {
                    while (flag) {
                        int next = random.nextInt(0, 100);
                        System.out.println("producer generate item:" + next);
                        QUEUE.put(next);
                    }
                } catch (InterruptedException e) {
                    System.out.println("interrupt producer");
                    throw new RuntimeException(e);
                } finally {
                    System.out.println("producer exit <<<");
                }
            }
        }
    }

    private static class CancelThreadWithInterruptTest implements ConsumeAndCancelProducerTest {
        private static final Integer QUEUE_SIZE = 10;
        private static final ArrayBlockingQueue<Integer> QUEUE = new ArrayBlockingQueue<>(QUEUE_SIZE);

        @Override
        public void consumerAndCancel() {
            var producer = new Producer();
            Thread producerThread = new Thread(producer);
            producerThread.start();
            //  等待生产者充满队列
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Random random = new Random(System.currentTimeMillis());
            System.out.println("consumer enter >>>");
            //  消费者随机退出
            while (random.nextInt(0, 100) > 10) {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("consumer consume:" + QUEUE.poll());
            }
            System.out.println("consumer exit <<<");
            System.out.println("notify producer exit start >>>");
            producerThread.interrupt();
            System.out.println("notify producer exit end <<<");
            System.out.println("producer thread status:" + producerThread.getState());
        }

        static class Producer implements Runnable {
            @Override
            public void run() {
                System.out.println("producer enter >>>");
                Random random = new Random(System.currentTimeMillis());
                try {
                    while (!Thread.currentThread().isInterrupted()) {
                        int next = random.nextInt(0, 100);
                        System.out.println("producer generate item:" + next);
                        QUEUE.put(next);
                    }
                } catch (InterruptedException e) {
                    System.out.println("interrupt producer");
                    throw new RuntimeException(e);
                } finally {
                    System.out.println("producer exit <<<");
                }
            }
        }
    }
}

/**
 * 测试类继承
 */
interface ConsumeAndCancelProducerTest {
    /**
     * 消费数据后 cancel producer
     */
    void consumerAndCancel();
}
